package cc.wfu.type.aop;

import cc.wfu.type.annotation.PreAntAuthorize;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;

@Slf4j
@Aspect
@Component
public class RoleAuthorizeAspect {

    @Pointcut("@annotation(cc.wfu.type.annotation.PreAntAuthorize)")
    public void pointCut() {}

    @Around("@annotation(preAntAuthorize)")
    public Object preRoleAuthorize(ProceedingJoinPoint joinPoint, PreAntAuthorize preAntAuthorize) throws Throwable {
        if (preAntAuthorize != null) {
            String[] roles = preAntAuthorize.antAuthorities();
            String channelValue = preAntAuthorize.value().getValue();
            if (roles.length > 0) {
                String rolesString = StringUtils.arrayToDelimitedString(roles, "','");
                String preAuthorizeExpression = channelValue + "('" + rolesString + "')";
                // 利用 Spring Security 的 SecureMethodInvocation 来进行权限检查
                FilterInvocation fi = new FilterInvocation(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(), new MockHttpServletResponse(), new MockFilterChain());
                SecurityExpressionRoot root = new WebSecurityExpressionRoot(SecurityContextHolder.getContext().getAuthentication(), fi);
                EvaluationContext context = new StandardEvaluationContext(root);
                ExpressionParser parser = new SpelExpressionParser();
                Boolean hasRole = parser.parseExpression(preAuthorizeExpression).getValue(context, Boolean.class);
                if (Boolean.FALSE.equals(hasRole)) {
                    throw new AccessDeniedException("Access Denied");
                }
            }
        }
        return joinPoint.proceed();
    }




}